#pragma once

#include <atomic>
#include <iostream>
#include <string>
#include <thread>
#include <cstring>
#include <cerrno>
#include <cstdlib>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "err.hpp"

namespace udp
{

const std::string fifo = "message_pipe";

class udp_client
{
public:
    udp_client(const std::string& sip, uint16_t sport)
        : _sock(0)
        , _fifo_fd(0) 
        , _sip(sip)
        , _sport(sport)
    {
        _sock = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sock < 0)
        {
            std::cerr << "socket error" << std::endl;
            exit(SOCKET_ERR);
        }
        std::cout << "socket success " << _sock << std::endl;

        // 客户端不需要手动绑定
        
        // 做到输入输出分离 输出重定向到管道中
        umask(00);
        if (mkfifo(fifo.c_str(), 0664) < 0)
        {
            std::cerr << "mkfifo error" << std::endl;
            exit(FIFO_ERR);
        }
        std::cout << "mkfifo success" << std::endl;

        _fifo_fd = open(fifo.c_str(), O_WRONLY);
        dup2(_fifo_fd, 1);
        
        _sender = std::thread(&udp_client::send, this);
        _recver = std::thread(&udp_client::recv, this);
    }

    ~udp_client()
    {
        _sender.join();
        _recver.join();

        close(_fifo_fd);
        close(_sock);
        unlink(fifo.c_str());
    }

    void send()
    {
        struct sockaddr_in server;
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = inet_addr(_sip.c_str());
        server.sin_port = htons(_sport);

        while (true)
        {
            std::string msg;
            std::cerr << "please input:> ";
            getline(std::cin, msg);
            sendto(_sock, msg.c_str(), msg.size(), 0, (struct sockaddr*)&server, sizeof(server));
        }
    }

    void recv()
    {
        while (true)
        {
            struct sockaddr_in tmp;
            socklen_t len = sizeof(tmp);
            char buf[1024] = {0};
            ssize_t s = recvfrom(_sock, buf, sizeof(buf), 0, (struct sockaddr*)&tmp, &len);

            if (s > 0) buf[s] = 0;
            else continue;
            // std::cout << "server echo# " << buf << std::endl;
            std::cout << buf << std::endl;
        }
    }

private:
    int _sock;
    int _fifo_fd;
    std::string _sip;
    uint16_t _sport;

    std::thread _sender;
    std::thread _recver;
};

}
